package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.dto.request.CouponUseBackReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponDto;
import com.jzo2o.api.orders.dto.request.OrderCancelReqDTO;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.client.MarkerClient;
import com.jzo2o.orders.manager.client.ServeClient;
import com.jzo2o.orders.manager.job.OrderJob;
import com.jzo2o.orders.manager.model.dto.request.OrderPageQueryReqDTO;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import com.jzo2o.redis.helper.CacheHelper;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {
  @Autowired
  private OrderStateMachine orderStateMachine;

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */

    @Autowired
    private CacheHelper cacheHelper;
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //1.构件查询条件   只查询id，符合覆盖索引，不会索引失效
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .select(Orders::getId)
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus());
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //2.查询订单列表
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        List<Orders> records = ordersPage.getRecords();
        //收集所有的订单id
        List<Long> ids = records.stream().map(Orders::getId).collect(Collectors.toList());
        if(CollUtils.isEmpty(ids)){
            return CollUtils.emptyList();
        }
        //根据ids查询所有的订单列表
//        List<Orders> ordersList = batchQuery(ids);
//        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(ordersList, OrderSimpleResDTO.class);
        String cacheKey=String.format(RedisConstants.RedisKey.ORDERS,currentUserId);
        List<OrderSimpleResDTO> orderSimpleResDTOS = cacheHelper.<Long, OrderSimpleResDTO>batchGet(cacheKey, ids, (nocacheIds, clazz) -> {
             //查询缓存没有命中的订单
            List<Orders> orders = batchQuery(nocacheIds);
            if(CollUtils.isEmpty(orders)){
                //返回要缓存的数据，数据为空，缓存空的hashmap，防止缓存击穿
                return new HashMap<>();
            }
            //将数据转为要存储的hashmap
            Map<Long, OrderSimpleResDTO> collect = orders.stream().collect(Collectors.toMap(Orders::getId, o -> BeanUtils.toBean(o, OrderSimpleResDTO.class)));
                    return collect;
                },
                OrderSimpleResDTO.class, RedisConstants.Ttl.ORDERS_PAGE_TTL
        );
        return orderSimpleResDTOS;

    }
    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderResDTO getDetail(Long id) {
//        Orders orders = queryById(id);
//        OrderResDTO orderResDTO = BeanUtil.toBean(orders, OrderResDTO.class);
        String currentSnapshotCache = orderStateMachine.getCurrentSnapshotCache(id.toString());
        OrderSnapshotDTO dto = JsonUtils.toBean(currentSnapshotCache, OrderSnapshotDTO.class);
        OrderResDTO orderResDTO = BeanUtil.toBean(dto, OrderResDTO.class);
        return orderResDTO;
    }

    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }
   @Autowired
   private IOrdersManagerService owner;
    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public void cancel(OrderCancelReqDTO dto) {
        Orders orders = this.getById(dto.getId());
        if(ObjectUtils.isNull(orders)){
            throw new BadRequestException("订单不存在");
        }
        //判断订单状态是否为未支付还是派单中
        List<Integer> cancelStatus= Arrays.asList(OrderStatusEnum.NO_PAY.getStatus(),OrderStatusEnum.DISPATCHING.getStatus());
        if(!cancelStatus.contains(orders.getOrdersStatus())){
            throw new BadRequestException("订单状态非法");
        }

        if(ObjectUtils.equals(orders.getOrdersStatus(),OrderStatusEnum.NO_PAY.getStatus())){
           //未支付
            owner.cancelOrdersByNoPay(dto.getId(),UserContext.currentUserId(),dto.getCancelReason(),false);
        }else{
            //派单中
            OrderJob xxljob = applicationContext.getBean(OrderJob.class);
            xxljob.cancelOrderByDispatching(orders,dto.getCancelReason(),UserContext.currentUser());
        }
    }
   @Autowired
   private IOrdersCanceledService ordersCanceledService;
    /**
     * 取消未支付的订单
     * @param id
     * @param cancelReason
     * @param b  //是否是系统自动取消 true是
     */
    @Override
    @GlobalTransactional
    public void cancelOrdersByNoPay(Long id,Long userId, String cancelReason, boolean b) {
        //添加取消订单记录
        OrdersCanceled canceled = new OrdersCanceled();
        canceled.setId(id);
        canceled.setCancelReason(cancelReason);
        if(!b){
           canceled.setCancellerId(UserContext.currentUserId());
           canceled.setCancelerName(UserContext.currentUser().getName());
           canceled.setCancellerType(UserContext.currentUser().getUserType());
        }
        canceled.setCancelTime(LocalDateTime.now());
        boolean save = ordersCanceledService.save(canceled);
        if(!save){
            throw new DBException("添加取消记录失败");
        }
        //修改订单状态
//        boolean update = this.lambdaUpdate()
//                 .eq(Orders::getId, id)
//                .set(Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus())
//                .update();
        Orders orders = this.getById(id);
        //说明使用了优惠券
        if(ObjectUtils.isNotNull(orders.getDiscountAmount())&&orders.getDiscountAmount().compareTo(BigDecimal.ZERO)>0){
            CouponUseBackReqDTO dto = new CouponUseBackReqDTO();
            CouponDto coupon = markerClient.getCouponByOrdersId(id);
            dto.setId(coupon.getId());
            dto.setOrdersId(id);
            dto.setUserId(userId);

            markerClient.useBack(dto);
        }
        orderStateMachine.changeStatus(orders.getUserId(),orders.getId().toString(), OrderStatusChangeEventEnum.CANCEL);
//        if(!update){
//            throw new DBException("订单修改失败");
//        }
    }

    @Autowired
    private IOrdersRefundService ordersRefundService;

    @Override
    public PageResult<OrderSimpleResDTO> pageQuery(OrderPageQueryReqDTO dto) {
        IPage page = new Page(dto.getPageNo(),dto.getPageSize());
        this.lambdaQuery()
                .select(Orders::getId)
                .eq(ObjectUtils.isNotNull(dto.getUserId()),Orders::getUserId,dto.getUserId())
                .eq(ObjectUtils.isNotNull(dto.getPayStatus()),Orders::getPayStatus,dto.getPayStatus())
                .eq(ObjectUtils.isNotNull(dto.getRefundStatus()),Orders::getRefundStatus,dto.getRefundStatus())
                .eq(ObjectUtils.isNotNull(dto.getOrdersStatus()),Orders::getOrdersStatus,dto.getOrdersStatus())
                .in(ObjectUtils.isNotNull(dto.getOrdersIdList()),Orders::getId,dto.getOrdersIdList())
                .eq(ObjectUtils.isNotNull(dto.getId()),Orders::getId,dto.getId())
                .ge(ObjectUtils.isNotNull(dto.getMinCreateTime()),Orders::getCreateTime,dto.getMinCreateTime())
                .le(ObjectUtils.isNotNull(dto.getMaxCreateTime()),Orders::getCreateTime,dto.getMaxCreateTime())
                .orderBy(dto.getOrderBy1()!=null,dto.getIsAsc1(),Orders::getCreateTime)
                .page(page);
        List<Orders> records = page.getRecords();
        PageResult<OrderSimpleResDTO> res=new PageResult<>();
        res.setPages(page.getPages());
        res.setTotal(page.getTotal());
        if(CollUtils.isEmpty(records)){
            return res;
        }
        List<Long> list = records.stream().map(Orders::getId).collect(Collectors.toList());
        List<Orders> orders = batchQuery(list);
        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtils.copyToList(orders, OrderSimpleResDTO.class);
        res.setList(orderSimpleResDTOS);
        return res;
    }
    @Autowired
    private ServeClient serveClient;
    @Autowired
    private MarkerClient markerClient;
    /**
     * 获取可用优惠券
     * @param serveId
     * @param purNum
     * @return
     */
    @Override
    public List<AvailableCouponsResDTO> getCoupons(Long serveId, Integer purNum) {
        //查询服务是否存在
        ServeAggregationResDTO serve = serveClient.findServeDetailById(serveId);
        if(serve==null||serve.getPrice()==null){
            throw new BadRequestException("服务不存在");
        }
        BigDecimal totalAmount = serve.getPrice().multiply(BigDecimal.valueOf(purNum));
        //查询可用的优惠卷列表
        List<AvailableCouponsResDTO> dtos = markerClient.getAvailable(totalAmount);
        if(CollUtils.isEmpty(dtos)){
            return CollUtils.emptyList();
        }
        return dtos;
    }
}
